#include <sstream>
#include "IRenderer.h"
#include "Serialize.h"
#include "TinyXML2.h"
#include "Library.h"
#include "Model.h"
#include "Avatar.h"
#include "Library.h"
#include "Game.h"
#include "SceneGraph.h"
#include "EntityController.h"
#include "LightNode.h"
#include "CameraNode.h"
#include "ModelNode.h"
#include "TransformationNode.h"
#include "UpdateTask.h"
#include "PhysicsTask.h"
#include "GraphicalComponent.h"
#include "OrientationComponent.h"
#include "SelectableComponent.h"
#include "RigidBodyComponent.h"
#include "StatusComponent.h"
#include "CombatComponent.h"
#include "BoundingSphere.h"
#include "BoundingBox.h"
#include "TriangleMesh.h"

namespace Serialize
{
	
	bool LoadTextures( tinyxml2::XMLElement* element )
	{	
		// Load Textures

		tinyxml2::XMLElement* textureList = element->FirstChildElement("TextureList");

		if (textureList)
		{
			for (tinyxml2::XMLElement* texture = textureList->FirstChildElement("Texture"); texture != NULL; texture = texture->NextSiblingElement())
			{
				std::string filename = texture->Attribute("Filename");
				if (!Game::getSingleton().LoadTexture(filename))
					return false;
			}
		}
		return true;
	}
	bool LoadWorlds( tinyxml2::XMLElement* element )
	{
		tinyxml2::XMLElement* worldList = element->FirstChildElement("WorldList");

		// All Worlds
		if (worldList)
		{
			// for each world
			for (tinyxml2::XMLElement* world = worldList->FirstChildElement("World"); world != NULL; world = world->NextSiblingElement())
			{
				std::string name = world->Attribute("Name");
				Game::getSingleton().CreatePathingWorld(name);
				// for each obstacle
				for (tinyxml2::XMLElement* world = worldList->FirstChildElement("Obstacle"); world != NULL; world = world->NextSiblingElement())
				{
					float radius = world->FloatAttribute("Radius");
					float x = world->FloatAttribute("X");
					float y = world->FloatAttribute("Y");
					float z = world->FloatAttribute("Z");

					// What?
					Pointer<Pathing::World> worldPtr;
					Game::getSingleton().RetrievePathingWorld(name,worldPtr);
					worldPtr->Obstacles().push_back(new Pathing::Obstacle());
				}
			}
		}
		return true;
	}
	bool LoadResource( tinyxml2::XMLElement* element )
	{
		Pointer<IResource> resource;
		HashedString name(element->Attribute("Name"));
		HashedString type(element->Attribute("Type"));

		if (type == Model::type)
		{
			resource = new Model();
		} else if (type == Avatar::type)
		{
			resource = new Avatar();
		}
		if (!resource->FromXML(element))
			return false;
		
		Library::getSingleton().AddResource(name, resource);
		return true;
	}
	bool LoadResources(tinyxml2::XMLElement* element)
	{
		tinyxml2::XMLElement* resourceList = element->FirstChildElement("ResourceList");

		const unsigned int count = resourceList->UnsignedAttribute("Count");
		unsigned int counter = 0;
	
		tinyxml2::XMLElement* resource = resourceList->FirstChildElement("Resource");
		while (counter < count)
		{
			if (!LoadResource(resource))
				return false;
		
			resource = resource->NextSiblingElement("Resource");
			++counter;
		}
	
		return true;
	}
	bool LoadController(tinyxml2::XMLElement* element, Entity* entity)
	{
		HashedString type(element->Attribute("Type"));
		
		if (type == "EntityController")
		{
			Pointer<EntityController> controller;
			Game::getSingleton().CreateController(entity, controller);
			
			controller->setWorld(Game::getSingleton()._getPathingWorlds()["Default"]);
			UpdateTask::getSingleton().RegisterUpdatable(controller);

			tinyxml2::XMLElement* behavior = element->FirstChildElement("Behavior");
			HashedString behaviorType(behavior->Attribute("Type"));
			
			Pointer<IBehavior> ptr;
			Game::getSingleton().CreateBehavior(behaviorType, controller, ptr);
			
			tinyxml2::XMLElement* behaviorEle = behavior->FirstChildElement(behaviorType.getString().c_str());
			ptr->FromXML(behaviorEle);
			if (ptr.isValid())
				controller->Behavior(ptr);

			for (tinyxml2::XMLElement* waypoint = element->FirstChildElement("Waypoint"); waypoint != NULL; waypoint = waypoint->NextSiblingElement("Waypoint"))
			{
				Maths::Vector3D waypointVector;

				std::stringstream attrib(std::string(waypoint->Attribute("Vector")));
				attrib >> waypointVector.X() >> waypointVector.Y() >> waypointVector.Z();
				
				controller->PushWaypoint(waypointVector);
			}
		}
		return true;
	}
	bool LoadEntity(tinyxml2::XMLElement* element)
	{
		HashedString entityName(element->Attribute("Name"));

		Pointer<Entity> entity = new Entity();
	
		SelectableComponent* selectable = 0;
		RigidBodyComponent* rigidbody = 0;
		GraphicalComponent* graphic = 0;
		OrientationComponent* orientation = 0;
		StatusComponent* status = 0;
		CombatComponent* combat = 0;

		for (tinyxml2::XMLElement* component = element->FirstChildElement("Component"); component != NULL; component = component->NextSiblingElement("Component"))
		{
			HashedString type(component->Attribute("Type"));
			if (type == GraphicalComponent::type)
			{
				HashedString modelName(component->FirstChildElement("Model")->Attribute("Name"));
				Pointer<IResource> resource = Library::getSingleton().getResource(Model::type, modelName);
				if (resource.isValid())
				{
					Pointer<Model> model;
					CopyPointer(resource, model);
					Pointer<IComponent> comp = graphic = new GraphicalComponent(entity,model);
					entity->RegisterComponent(GraphicalComponent::type, comp);
				}
			} else if (type == CombatComponent::type)
			{
				combat = new CombatComponent(entity);
				
				/**********************
				read in effects and abilities
				***********************/

				//combat->Combatant()->Effects()[HealthCondition::type].push_back(new HealthCondition(entity, combat->Combatant()));
				//combat->Combatant()->Abilities().push_back(new MeleeAutoAttack(combat->Combatant()));

				for (tinyxml2::XMLElement* child = element->FirstChildElement(); child != NULL; child = child->NextSiblingElement())
				{
					if (child->Name() == "Effect")
					{
						HashedString name(child->Attribute("Name"));
						HashedString type(child->Attribute("Type"));

						Pointer<Combat::IEffect> effect;
						Game::getSingleton().CreateEffect(name, combat->Combatant(), effect);

						combat->Combatant()->Effects()[type].push_back(effect);
					} else if (child->Name() == "Ability")
					{
						HashedString name(child->Attribute("Name"));

						Pointer<Combat::IAbility> ability;
						Game::getSingleton().CreateAbility(name, combat->Combatant(), ability);
						
						combat->Combatant()->Abilities().push_back(ability);
					}
				}

				entity->RegisterComponent(CombatComponent::type, Pointer<IComponent>(combat));
			} else if (type == StatusComponent::type)
			{
				status = new StatusComponent(entity);
				
				for (tinyxml2::XMLElement* stats = component->FirstChildElement("Status"); stats != NULL; stats = stats->NextSiblingElement("Status"))
				{
					status->getStatus(stats->Attribute("Name")) = stats->IntAttribute("Value");
				}

				entity->RegisterComponent(StatusComponent::type, Pointer<IComponent>(status));
			} else if (type == OrientationComponent::type)
			{
				orientation = new OrientationComponent(entity);
				tinyxml2::XMLElement* posXML = component->FirstChildElement("Position");
				tinyxml2::XMLElement* rotXML = component->FirstChildElement("Rotation");
				Maths::Vector3D position, rotation;
				{
					std::stringstream attrib(std::string(posXML->Attribute("Vector")));
					attrib >> position.X() >> position.Y() >> position.Z();
				}
				{
					std::stringstream attrib(std::string(rotXML->Attribute("Vector")));
					attrib >> rotation.X() >> rotation.Y() >> rotation.Z();
				}
				orientation->getPosition() = position;
				orientation->getRotation() = Maths::Quaternion(rotation);

				if (rigidbody)
				{
					rigidbody->getPosition() = new DoubleValueTrigger<Maths::Vector3D>(&orientation->getPosition(), &rigidbody->getBody()->getPosition());
					rigidbody->getBody()->AddShape(Pointer<IShape>(new BoundingSphere(1.0f)));
				}
				if (selectable)
				{
					selectable->getPosition() = new DoubleValueTrigger<Maths::Vector3D>(&orientation->getPosition(), &selectable->getBody()->getPosition());
					//selectable->getBody()->AddShape(Pointer<IShape>(new BoundingSphere(1.0f)));
					Game::getSingleton()._getSelectables()->AddRigidBody(selectable->getBody());
				}

				entity->RegisterComponent(OrientationComponent::type, Pointer<IComponent>(orientation));
			} else if (type == SelectableComponent::type)
			{
				selectable = new SelectableComponent(Pointer<RigidBody>(new RigidBody(1.0f, entity)), entity);
				for (tinyxml2::XMLElement* shape = component->FirstChildElement("Shape"); shape != NULL; shape = shape->NextSiblingElement("Shape"))
				{
					HashedString shapeType(shape->Attribute("Type"));
					if (shapeType == BoundingSphere::type)
					{
						float radius;
						Maths::Vector3D offset;
						{
							std::stringstream attrib(std::string(shape->Attribute("Offset")));
							attrib >> offset.X() >> offset.Y() >> offset.Z();
						}
						{
							std::stringstream attrib(std::string(shape->Attribute("Detail")));
							attrib >> radius;
						}
						Pointer<IShape> ishape = new BoundingSphere(radius);
						ishape->setOffset(offset, Maths::Quaternion());
						selectable->getBody()->AddShape(ishape);
					} else if (shapeType == TriangleMesh::type && graphic)
					{
						ModelNode* modelNode = (ModelNode*)((INode*)graphic->getNode());
						Model* model = modelNode->getModelInstance()->getModel();
						Mesh::Vector_t& meshes = model->getMeshes();
					
						Pointer<IShape> mesh;
						for (Mesh::Vector_t::iterator it = meshes.begin(); it != meshes.end(); ++it)
						{
							unsigned int offset, size, vertSize = (*it)->getVertexContainer()->getVertexDeclaration().m_vertexSize;
	#pragma region FIND_ENUMPOSITION
							{
								const VertexType::Vector_t& params = (*it)->getVertexContainer()->getVertexDeclaration().m_parameters;
								unsigned int paramSize = params.size();

								for (unsigned int i = 0; i < paramSize; ++i)
								{
									if (params[i].m_parameter == enumPosition)
									{
										offset = params[i].m_offset;
										size = params[i].m_size;
										break;
									}
								}
							}
	#pragma endregion

							std::vector<float> verts;

							unsigned char* ptr = (unsigned char*)((*it)->getVertexContainer()->getVertexPointer()) + offset;
							unsigned int count = (*it)->getVertexContainer()->getVerticeCount();

							for (unsigned int i = 0; i < count; ++i)		// for each vertex
							{
								for (unsigned int j = 0; j < size; ++j)
									verts.push_back(*(((float*)ptr) + j));
								ptr += vertSize;
							}

							mesh = new TriangleMesh(&verts[0], count/3);
							selectable->getBody()->setMass(0.0f);
							selectable->getBody()->AddShape(mesh);
						}
					}
				}
			
				if (orientation)
				{
					selectable->getPosition() = new DoubleValueTriggerFunctor<Maths::Vector3D, RigidBody>(&orientation->getPosition(), &selectable->getBody()->getPosition(), selectable->getBody(), &RigidBody::setPosition);
					Game::getSingleton()._getSelectables()->AddRigidBody(selectable->getBody());
				}
				entity->RegisterComponent(SelectableComponent::type, Pointer<IComponent>(selectable));
			} else if (type == RigidBodyComponent::type)
			{
				Pointer<IShape> mesh;
				GraphicalComponent* graphical = (GraphicalComponent*)entity->FindComponent(GraphicalComponent::type);
				RigidBodyComponent* rigidbody = new RigidBodyComponent(Pointer<RigidBody>(new RigidBody(1.0f, entity)), entity);
				
				BoundingSphere* sphere = new BoundingSphere(1.0f);
				mesh = sphere;
				rigidbody->getBody()->AddShape(mesh);

				System* world = PhysicsTask::getSingleton().FindSystem(HashedString("World"));
				if (world)
					world->AddRigidBody(rigidbody->getBody());
				
				for (tinyxml2::XMLElement* shapeEle = component->FirstChildElement("Shape"); shapeEle != NULL; shapeEle = shapeEle->NextSiblingElement("Shape"))
				{
					HashedString shape(shapeEle->Attribute("Type"));
				
					if (shape == BoundingSphere::type || shape == HashedString("Sphere"))
					{
						BoundingSphere* sphere = new BoundingSphere(1.0f);
						mesh = sphere;
					}
					if (shape == TriangleMesh::type)					// Must be static
					{
						ModelNode* modelNode = (ModelNode*)((INode*)graphical->getNode());
						Model* model = modelNode->getModelInstance()->getModel();
						Mesh::Vector_t& meshes = model->getMeshes();

						for (Mesh::Vector_t::iterator it = meshes.begin(); it != meshes.end(); ++it)
						{
							unsigned int offset, size, vertSize = (*it)->getVertexContainer()->getVertexDeclaration().m_vertexSize;
	#pragma region FIND_ENUMPOSITION
							{
								const VertexType::Vector_t& params = (*it)->getVertexContainer()->getVertexDeclaration().m_parameters;
								unsigned int paramSize = params.size();

								for (unsigned int i = 0; i < paramSize; ++i)
								{
									if (params[i].m_parameter == enumPosition)
									{
										offset = params[i].m_offset;
										size = params[i].m_size;
										break;
									}
								}
							}
	#pragma endregion

							std::vector<float> verts;

							unsigned char* ptr = (unsigned char*)((*it)->getVertexContainer()->getVertexPointer()) + offset;
							unsigned int count = (*it)->getVertexContainer()->getVerticeCount();

							for (unsigned int i = 0; i < count; ++i)		// for each vertex
							{
								for (unsigned int j = 0; j < size; ++j)
									verts.push_back(*(((float*)ptr) + j));
								ptr += vertSize;
							}

							mesh = new TriangleMesh(&verts[0], count/3);
							rigidbody->getBody()->setMass(0.0f);
						}
					}
					if (mesh)
					{
						rigidbody->getBody()->AddShape(mesh);
					}
				}
				entity->RegisterComponent(RigidBodyComponent::type, Pointer<IComponent>(rigidbody));
				if (orientation)
				{
					rigidbody->getPosition() = new DoubleValueTrigger<Maths::Vector3D>(&orientation->getPosition(), &rigidbody->getBody()->getPosition());
				}
				rigidbody->getBody()->RemoveShape(0);
			}
		}
		Game::getSingleton()._getAllEntities().insert(Entity::Pair_t(entityName, entity));

		tinyxml2::XMLElement* controller = element->FirstChildElement("EntityController");
		if (controller)
			if (!LoadController(controller, entity))
				return false;
		return true;
	}
	bool LoadEntities(tinyxml2::XMLElement* element)
	{
		tinyxml2::XMLElement* entityList = element->FirstChildElement("EntityList");

		unsigned int count = entityList->UnsignedAttribute("Count");
		for (tinyxml2::XMLElement* entity = entityList->FirstChildElement("Entity"); entity != NULL; entity = entity->NextSiblingElement())
			LoadEntity(entity);
		return true;
	}
	bool LoadNode(INode* workingNode, tinyxml2::XMLElement* element)
	{
		std::string type(element->Attribute("Type"));
		Pointer<INode> thisNode;
		if (type == "Root")
		{
			for (tinyxml2::XMLElement* node = element->FirstChildElement("Node"); node != NULL; node = node->NextSiblingElement("Node"))
			{
				if (!LoadNode(SceneGraph::getSingletonPtr(), node))
					return false;
			}

			return true;
		} else if (type == "CameraNode")
		{	
			CameraNode* camera;
			thisNode = camera = new CameraNode();

			Pointer<INode> _cameraNode(thisNode);
			CopyPointer(_cameraNode, Game::getSingleton()._getActiveCamera());
			workingNode->Push_Back(thisNode);
		
			bool isPerspective = element->BoolAttribute("IsPerspective");

			tinyxml2::XMLElement* posXML = element->FirstChildElement("Position");
			tinyxml2::XMLElement* rotXML = element->FirstChildElement("Rotation");
			Maths::Vector3D position;
			Maths::Vector3D rotation;
			{
				std::stringstream attrib(std::string(posXML->FirstChild()->ToText()->Value()));
				attrib >> position.X() >> position.Y() >> position.Z();
			}
			{
				std::stringstream attrib(std::string(rotXML->FirstChild()->ToText()->Value()));
				attrib >> rotation.X() >> rotation.Y() >> rotation.Z();
			}

			camera->setPosition(position);
			camera->setRotation(rotation);
		
			tinyxml2::XMLElement* nearXML = element->FirstChildElement("Near");
			tinyxml2::XMLElement* farXML = element->FirstChildElement("Far");
		
			{
				std::stringstream attrib(std::string(nearXML->FirstChild()->ToText()->Value()));
				attrib >> camera->Near();
			}
			{
				std::stringstream attrib(std::string(farXML->FirstChild()->ToText()->Value()));
				attrib >> camera->Far();
			}

			if (isPerspective)
			{
				camera->setProjection();
				tinyxml2::XMLElement* fovyXML = element->FirstChildElement("Fovy");
				float aspect = Renderer->getWindowWidth()/(float)(Renderer->getWindowHeight());

				{
					std::stringstream attrib(std::string(fovyXML->FirstChild()->ToText()->Value()));
					attrib >> camera->Fovy();
				}
				camera->Aspect() = aspect;
			} else 
			{
				camera->setOrtho();
				tinyxml2::XMLElement* topXML = element->FirstChildElement("Top");
				tinyxml2::XMLElement* bottomXML = element->FirstChildElement("Bottom");
				tinyxml2::XMLElement* rightXML = element->FirstChildElement("Right");
				tinyxml2::XMLElement* leftXML = element->FirstChildElement("Left");
			
				{
					std::stringstream attrib(std::string(topXML->FirstChild()->ToText()->Value()));
					attrib >> camera->Top();
				}
				{
					std::stringstream attrib(std::string(bottomXML->FirstChild()->ToText()->Value()));
					attrib >> camera->Bottom();
				}
				{
					std::stringstream attrib(std::string(leftXML->FirstChild()->ToText()->Value()));
					attrib >> camera->Left();
				}
				{
					std::stringstream attrib(std::string(rightXML->FirstChild()->ToText()->Value()));
					attrib >> camera->Right();
				}
			}
		} 
		else if (type == "Entity")
		{
			HashedString name(element->Attribute("Name"));
			HashedString component(element->Attribute("Component"));
			Entity::Map_t::iterator entity = Game::getSingleton()._getAllEntities().find(name);
			if (component == OrientationComponent::type)
			{
				OrientationComponent* orientation = (OrientationComponent*)(entity->second->FindComponent(OrientationComponent::type));
				thisNode = orientation->getTransformNode();
				workingNode->Push_Back(thisNode);
			} else if (component == GraphicalComponent::type)
			{
				GraphicalComponent* graphic = (GraphicalComponent*)(entity->second->FindComponent(GraphicalComponent::type));
				thisNode = graphic->getNode();
				workingNode->Push_Back(thisNode);
			}
		}
		else if (type == "LightNode")
		{
			LightNode* lightNode = new LightNode();
			Light& light = lightNode->getLight();

			tinyxml2::XMLElement* posXML = element->FirstChildElement("Position");
			tinyxml2::XMLElement* ambientXML = element->FirstChildElement("Ambient");
			tinyxml2::XMLElement* diffuseXML = element->FirstChildElement("Diffuse");
			tinyxml2::XMLElement* specularXML = element->FirstChildElement("Specular");
			tinyxml2::XMLElement* spotDirXML = element->FirstChildElement("SpotDirection");
			tinyxml2::XMLElement* spotExpoXML = element->FirstChildElement("SpotExponent");
			tinyxml2::XMLElement* spotCutOffXML = element->FirstChildElement("SpotCutOff");
			tinyxml2::XMLElement* constAttenXML = element->FirstChildElement("ConstAttenuation");
			tinyxml2::XMLElement* linAttenXML = element->FirstChildElement("LinearAttenuation");
			tinyxml2::XMLElement* quadAttenXML = element->FirstChildElement("QuadraticAttenuation");
		
			{
				std::string test(posXML->FirstChild()->ToText()->Value());
				std::stringstream attrib(test);
				for (int i = 0; i < 4; ++i)
					attrib >> light.m_Position[i];
			}
			{
				std::stringstream attrib(std::string(ambientXML->FirstChild()->ToText()->Value()));
				for (int i = 0; i < 4; ++i)
					attrib >> light.m_Ambient[i];
			}
			{
				std::stringstream attrib(std::string(diffuseXML->FirstChild()->ToText()->Value()));
				for (int i = 0; i < 4; ++i)
					attrib >> light.m_Diffuse[i];
			}
			{
				std::stringstream attrib(std::string(specularXML->FirstChild()->ToText()->Value()));
				for (int i = 0; i < 4; ++i)
					attrib >> light.m_Specular[i];
			}
			{
				std::stringstream attrib(std::string(spotDirXML->FirstChild()->ToText()->Value()));
				for (int i = 0; i < 3; ++i)
					attrib >> light.m_SpotDirection[i];
			}
			{
				std::stringstream attrib(std::string(spotExpoXML->FirstChild()->ToText()->Value()));
				attrib >> light.m_SpotExponent;
			}
			{
				std::stringstream attrib(std::string(spotCutOffXML->FirstChild()->ToText()->Value()));
				attrib >> light.m_SpotCutOff;
			}
			{
				std::stringstream attrib(std::string(constAttenXML->FirstChild()->ToText()->Value()));
				attrib >> light.m_ConstAttenuation;
			}
			{
				std::stringstream attrib(std::string(linAttenXML->FirstChild()->ToText()->Value()));
				attrib >> light.m_LinearAttenuation;
			}
			{
				std::stringstream attrib(std::string(quadAttenXML->FirstChild()->ToText()->Value()));
				attrib >> light.m_QuadraticAttenuation;
			}
			thisNode = lightNode;
			workingNode->Push_Back(thisNode);
		}
		if (thisNode && element->Attribute("Name"))
		{
			std::string nodeName(element->Attribute("Name"));
			Game::getSingleton().RememberNode(nodeName, thisNode);
		}

		for (tinyxml2::XMLElement* node = element->FirstChildElement("Node"); node != NULL; node = node->NextSiblingElement("Node"))
		{
			if (!LoadNode(thisNode, node))
				return false;
		}

		return true;
	}
	bool LoadSceneGraph(tinyxml2::XMLElement* element)
	{
		tinyxml2::XMLElement* sceneGraph = element->FirstChildElement("SceneGraph");

		for (INode::Vector_t::iterator it = SceneGraph::getSingleton().getChildren().begin(); it != SceneGraph::getSingleton().getChildren().end();)
		{
			if ((*it)->Type() != HashedString("GUINode"))
			{
				(*it)->Remove();
				it = SceneGraph::getSingleton().getChildren().begin();
			} else
			{
				++it;
			}
		}
		
		for (tinyxml2::XMLElement* node = sceneGraph->FirstChildElement("Node"); node != NULL; node = node->NextSiblingElement("Node"))
		{
			if (!LoadNode(0, node))
				return false;
		}
		return true;
	}
	bool Load( std::string filename )
	{
		tinyxml2::XMLDocument doc;
		if (doc.LoadFile(filename.c_str()) == tinyxml2::XML_ERROR_FILE_NOT_FOUND)
			return false;

		tinyxml2::XMLElement* rootNode = doc.RootElement();

		// Textures
		if (!LoadTextures(rootNode))
			return false;
		// Pathing Worlds
		if (!LoadWorlds(rootNode))
			return false;
		// Load resources
		if (!LoadResources(rootNode))
			return false;
		// Load Entities
		if (!LoadEntities(rootNode))
			return false;
		// Load SceneGraph
		if (!LoadSceneGraph(rootNode))
			return false;
		return true;
	}
	bool SaveNode( INode* node, tinyxml2::XMLPrinter& printer )
	{		
		if (node->Type() == HashedString("GUINode"))
		{
			return true;
		}
		printer.OpenElement("Node");
		if (node->Type() == HashedString("TransformationNode"))
		{	
			Entity::Map_t::iterator it;
			for (it = Game::getSingleton()._getAllEntities().begin(); it != Game::getSingleton()._getAllEntities().end(); ++it)
			{
				HashedString name(it->first);
				Entity* entity = it->second;
				OrientationComponent* component = (OrientationComponent*)(entity->FindComponent(OrientationComponent::type));
				if (component)
				{
					if ((void*)(component->getTransformNode()) == (void*)node)
					{
						printer.PushAttribute("Type", "Entity");
						printer.PushAttribute("Name", name.getString().c_str());
						printer.PushAttribute("Component", OrientationComponent::type.getString().c_str());
						break;
					}
				}
			}
			if (it == Game::getSingleton()._getAllEntities().end())
			{
				printer.PushAttribute("Type", node->Type().getString().c_str());
				TransformationNode* transform = (TransformationNode*)node;

				Maths::Vector3D& position = ((ValueTrigger<Maths::Vector3D>*)(TriggerBase*)transform->getPosition())->getObject();
				Maths::Quaternion& rotation = ((ValueTrigger<Maths::Quaternion>*)(TriggerBase*)transform->getRotation())->getObject();
			
				printer.OpenElement("Position");
				printer.PushText(position.X());
				printer.PushText(" ");
				printer.PushText(position.Y());
				printer.PushText(" ");
				printer.PushText(position.Z());
				printer.CloseElement();

				printer.OpenElement("Rotation");
				printer.PushText(rotation.X());
				printer.PushText(" ");
				printer.PushText(rotation.Y());
				printer.PushText(" ");
				printer.PushText(rotation.Z());
				printer.PushText(" ");
				printer.PushText(rotation.W());
				printer.CloseElement();
			}
		}
		else if (node->Type() == HashedString("ModelNode"))
		{
			ModelNode* model = (ModelNode*)((INode*)node);
			Entity::Map_t::iterator it;
			for (it = Game::getSingleton()._getAllEntities().begin(); it != Game::getSingleton()._getAllEntities().end(); ++it)
			{
				HashedString name(it->first);
				Entity* entity = it->second;
				GraphicalComponent* component = (GraphicalComponent*)(entity->FindComponent(GraphicalComponent::type));
				if (component)
				{
					if ((void*)(component->getNode()) == (void*)node)
					{
						printer.PushAttribute("Type", "Entity");
						printer.PushAttribute("Name", name.getString().c_str());
						printer.PushAttribute("Component", GraphicalComponent::type.getString().c_str());
						break;
					}
				}
			}
		
			if (it == Game::getSingleton()._getAllEntities().end())
			{
				printer.PushAttribute("Type", "ModelNode");
				Pointer<IResource> resource;
				CopyPointer(model->getModelInstance()->getModel(), resource);
				HashedString modelName(Library::getSingleton().getResourceName(Model::type, resource));
				printer.PushAttribute("Model", modelName.getString().c_str());
			}
		}
		else if (node->Type() == HashedString("LightNode"))
		{
			LightNode* lightNode = (LightNode*)node;
			Light& light = lightNode->getLight();
		
			printer.PushAttribute("Type", node->Type().getString().c_str());
			printer.OpenElement("Position");
			for (int i = 0; i <3; ++i)
			{
				printer.PushText(light.m_Position[i]);
				printer.PushText(" ");
			}
			printer.PushText(light.m_Position[3]);
			printer.CloseElement();

			printer.OpenElement("Ambient");
			for (int i = 0; i < 3; ++i)
			{
				printer.PushText(light.m_Ambient[i]);
				printer.PushText(" ");
			}
			printer.PushText(light.m_Ambient[3]);
			printer.CloseElement();

			printer.OpenElement("Diffuse");
			for (int i = 0; i < 3; ++i)
			{
				printer.PushText(light.m_Diffuse[i]);
				printer.PushText(" ");
			}
			printer.PushText(light.m_Diffuse[3]);
			printer.CloseElement();

			printer.OpenElement("Specular");
			for (int i = 0; i < 3; ++i)
			{
				printer.PushText(light.m_Specular[i]);
				printer.PushText(" ");
			}
			printer.PushText(light.m_Specular[3]);
			printer.CloseElement();
			printer.OpenElement("SpotDirection");
			for (int i = 0; i < 2; ++i)
			{
				printer.PushText(light.m_SpotDirection[i]);
				printer.PushText(" ");
			}
			printer.PushText(light.m_SpotDirection[2]);
			printer.CloseElement();
			printer.OpenElement("SpotExponent");
			printer.PushText(light.m_SpotExponent);
			printer.CloseElement();
			printer.OpenElement("SpotCutOff");
			printer.PushText(light.m_SpotCutOff);
			printer.CloseElement();
			printer.OpenElement("ConstAttenuation");
			printer.PushText(light.m_ConstAttenuation);
			printer.CloseElement();
			printer.OpenElement("LinearAttenuation");
			printer.PushText(light.m_LinearAttenuation);
			printer.CloseElement();
			printer.OpenElement("QuadraticAttenuation");
			printer.PushText(light.m_QuadraticAttenuation);
			printer.CloseElement();
		}
		else if (node->Type() == HashedString("Root"))
		{
			printer.PushAttribute("Type", "Root");
		}
		else if (node->Type() == HashedString("CameraNode"))
		{
			CameraNode* camera = (CameraNode*)node;
			printer.PushAttribute("Type", "CameraNode");
			printer.PushAttribute("IsPerspective", camera->IsProjection());
			Maths::Vector3D position(camera->Position());
			Maths::Vector3D rotation(camera->Rotation());
			printer.OpenElement("Position");
			printer.PushText(position.X());
			printer.PushText(" ");
			printer.PushText(position.Y());
			printer.PushText(" ");
			printer.PushText(position.Z());
			printer.CloseElement();

			printer.OpenElement("Rotation");
			printer.PushText(rotation.X());
			printer.PushText(" ");
			printer.PushText(rotation.Y());
			printer.PushText(" ");
			printer.PushText(rotation.Z());
			printer.CloseElement();

			printer.OpenElement("Near");
			printer.PushText(camera->Near());
			printer.CloseElement();
			printer.OpenElement("Far");
			printer.PushText(camera->Far());
			printer.CloseElement();
			if (camera->IsProjection())
			{
				printer.OpenElement("Fovy");
				printer.PushText(camera->Fovy());
				printer.CloseElement();
			} else
			{
				printer.OpenElement("Left");
				printer.PushText(camera->Left());
				printer.CloseElement();
				printer.OpenElement("Right");
				printer.PushText(camera->Right());
				printer.CloseElement();
				printer.OpenElement("Top");
				printer.PushText(camera->Top());
				printer.CloseElement();
				printer.OpenElement("Bottom");
				printer.PushText(camera->Bottom());
				printer.CloseElement();
			}
		}
		for (INode::Vector_t::iterator it = node->getChildren().begin(); it != node->getChildren().end(); ++it)
		{
			SaveNode((*it), printer);
		}
		printer.CloseElement();
		return true;
	}
	bool SaveSceneGraph( tinyxml2::XMLPrinter& printer )
	{
		// SceneGraph

		printer.OpenElement("SceneGraph");

		SaveNode(SceneGraph::getSingletonPtr(), printer);
	
		printer.CloseElement();
		return true;
	}
	bool SaveComponent( const IComponent::Pair_t& componentPair, tinyxml2::XMLPrinter& printer )
	{
		const HashedString& type (componentPair.first);
		IComponent* component = (componentPair.second);
	
		printer.OpenElement("Component");
		printer.PushAttribute("Type", type.getString().c_str());

		component->ToXML(printer);

		printer.CloseElement();
		return true;
	}
	bool SaveEntity( const Entity::Pair_t& entityPair, tinyxml2::XMLPrinter& printer )
	{
		const HashedString& name(entityPair.first);
		Entity* entity = (entityPair.second);
		
		printer.OpenElement("Entity");

		printer.PushAttribute("Name", name.getString().c_str());		
		// Entity linked with Avatar?
		std::map<HashedString, std::vector<Entity*>>& avatarEntityMap =  Game::getSingleton()._getAvatarEntityMap();

		HashedString avatarName;

		for (std::map<HashedString, std::vector<Entity*>>::iterator it1 = avatarEntityMap.begin(); it1 != avatarEntityMap.end(); ++it1)
		{
			for (std::vector<Entity*>::iterator it2 = it1->second.begin(); it2 != it1->second.end(); ++it2)
			{
				if ((*it2) == entity)
				{
					avatarName = it1->first;
					break;
				}
			}
			if (avatarName != HashedString())
			{
				break;
			}
		}

		if (avatarName != HashedString())
			printer.PushAttribute("Avatar", entity->Components().size());
		else
		{
			printer.PushAttribute("Components", entity->Components().size());
			
			for (IComponent::Map_t::reverse_iterator it = entity->Components().rbegin(); it != entity->Components().rend(); ++it)
			{
				SaveComponent(*it, printer);
			}
		}

		// EntityController
		EntityController* controller = 0;

		for (EntityController::Vector_t::iterator it = Game::getSingleton()._getControllers().begin(); it != Game::getSingleton()._getControllers().end(); ++it)
		{
			if ((*it)->getEntity() == entity)
			{
				controller = (*it);
			}
		}
		
		if (controller)
		{
			printer.OpenElement("EntityController");
			controller->ToXML(printer);
			printer.CloseElement();
		}

		printer.CloseElement();
		return true;
	}
	bool SaveEntities( tinyxml2::XMLPrinter& printer )
	{
		//Entity Header
		printer.OpenElement("EntityList");
		printer.PushAttribute("Count", Game::getSingleton()._getAllEntities().size());

		// Entities
		for (Entity::Map_t::iterator it = Game::getSingleton()._getAllEntities().begin(); it != Game::getSingleton()._getAllEntities().end(); ++it)
		{
			const Entity::Pair_t& pair(*it);
			SaveEntity(pair, printer);
		}

		printer.CloseElement();
		return true;
	}
	bool SaveTextures( tinyxml2::XMLPrinter& printer )
	{
		printer.OpenElement("TextureList");
		printer.PushAttribute("Count", Game::getSingleton()._getTextureStrings().size());

		for (std::vector<std::string>::iterator it = Game::getSingleton()._getTextureStrings().begin(); it != Game::getSingleton()._getTextureStrings().end(); ++it)
		{
			printer.OpenElement("Texture");
			printer.PushAttribute("Filename", (*it).c_str());
			printer.CloseElement();
		}

		printer.CloseElement();
		return true;
	}
	bool SavePathingWorlds( tinyxml2::XMLPrinter& printer )
	{
		printer.OpenElement("WorldList");
		printer.PushAttribute("Count", Game::getSingleton()._getPathingWorlds().size());
		// Worlds
		for (Pathing::World::Map_t::iterator it = Game::getSingleton()._getPathingWorlds().begin(); it != Game::getSingleton()._getPathingWorlds().end(); ++it)
		{
			printer.OpenElement("World");
			printer.PushAttribute("Name", it->first.getString().c_str());
			printer.PushAttribute("Obstacles", it->second->Obstacles().size());
			
			for (Pathing::Obstacle::Vector_t::iterator it1 = it->second->Obstacles().begin(); it1 != it->second->Obstacles().end(); ++it1)
			{
				printer.OpenElement("Obstacle");
				printer.PushAttribute("Radius", (*it1)->Radius());
				printer.PushAttribute("X", (*it1)->Position().X());
				printer.PushAttribute("Y", (*it1)->Position().Y());
				printer.PushAttribute("Z", (*it1)->Position().Z());

				printer.CloseElement();
			}
			
			printer.CloseElement();
		}
		printer.CloseElement();
		return true;
	}
	bool SaveResource( const Library::ResourcesPair_t& resource, tinyxml2::XMLPrinter& printer)
	{
		HashedString type(resource.first);
		const IResource::Map_t& resources = resource.second;

		for (IResource::Map_t::const_iterator it = resources.begin(); it != resources.end(); ++it)
		{
			HashedString name(it->first);

			printer.OpenElement("Resource");
			printer.PushAttribute("Type", type.getString().c_str());
			printer.PushAttribute("Name", name.getString().c_str());

			IResource* resourcePtr = it->second;
			resourcePtr->ToXML(printer);
		
			printer.CloseElement();
		}
		return true;
	}
	bool SaveResources( tinyxml2::XMLPrinter& printer )
	{
		// Resource List
		printer.OpenElement("ResourceList");
		printer.PushAttribute("Count", Library::getSingleton().ResourceCount());

		for (Library::ResourcesMap_t::iterator it = Library::getSingleton().getResources().begin(); it != Library::getSingleton().getResources().end(); ++it)
		{
			if (!SaveResource(*it, printer))
				return false;
		}

		printer.CloseElement();

		return true;
	}
	bool Save( std::string filename )
	{
		FILE* fp;
		fopen_s(&fp, filename.c_str(), "w");

		if (fp==NULL)
			return false;

		tinyxml2::XMLPrinter printer(fp);
		// State
		printer.OpenElement("State");
		// Texture List
		// Pathing Worlds
		// Resource List
		if (!SaveResources(printer))
			return false;
		// Entity List
		// SceneGraph

		printer.CloseElement();
		
		fclose(fp);

		return true;
	}
};